package com.dy.yunying.biz.controller.usergroup;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dy.yunying.api.entity.WanGameDO;
import com.dy.yunying.api.entity.usergroup.UserGroup;
import com.dy.yunying.api.entity.usergroup.UserGroupData;
import com.dy.yunying.api.entity.usergroup.UserGroupLog;
import com.dy.yunying.api.enums.UserGroupBindInfoQryTypeEnum;
import com.dy.yunying.api.enums.UserGroupStatusEnum;
import com.dy.yunying.api.enums.UserGroupTypeEnum;
import com.dy.yunying.api.req.usergroup.UserGroupReq;
import com.dy.yunying.api.req.usergroup.UserGroupStatReq;
import com.dy.yunying.api.req.usergroup.UserGroupUpdateStatusReq;
import com.dy.yunying.api.vo.usergroup.UserGroupNoticePriceVO;
import com.dy.yunying.api.vo.usergroup.UserGroupStatVO;
import com.dy.yunying.api.vo.usergroup.UserGroupVo;
import com.dy.yunying.biz.config.YunYingProperties;
import com.dy.yunying.biz.service.manage.GameService;
import com.dy.yunying.biz.service.usergroup.UserGroupDataService;
import com.dy.yunying.biz.service.usergroup.UserGroupLogService;
import com.dy.yunying.biz.service.usergroup.UserGroupService;
import com.dy.yunying.biz.utils.OEHttpUtils;
import com.dy.yunying.biz.utils.RedisLock;
import com.pig4cloud.pig.admin.api.entity.SysUser;
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
import com.pig4cloud.pig.api.util.NoRepeatSubmit;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户群组
 * @author zhuxm
 * @date 2022-04-25 15:11:05
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/userGroup")
@Api(value = "userGroup", tags = "用户管理-用户群组")
@Slf4j
public class UserGroupController {

	private final UserGroupService userGroupService;
	private final RemoteUserService remoteUserService;
	private final GameService gameService;


	private final UserGroupLogService userGroupLogService;

	private final UserGroupDataService userGroupDataService;

	private final RedisLock redisLock;

	@Resource
	private YunYingProperties yunYingProperties;


	@ApiOperation(value = "用户群组列表", notes = "用户群组列表")
	@RequestMapping("/selectPage")
	public R selectPage(@Valid @RequestBody UserGroupReq req) {
		try {
			QueryWrapper<UserGroup> query = new QueryWrapper<>();

			query.lambda().and(StringUtils.isNotBlank(req.getGroupName()), wrapper -> wrapper.eq(UserGroup::getId, req.getGroupName()).or().like(UserGroup::getGroupName, req.getGroupName()))
					.eq(req.getGroupType() != null, UserGroup::getGroupType, req.getGroupType())
					.eq(req.getGroupClass() != null, UserGroup::getGroupClass, req.getGroupClass())
					.eq(req.getClassGameid() != null, UserGroup::getClassGameid, req.getClassGameid())
					.eq(req.getDimension() != null, UserGroup::getDimension, req.getDimension())
					.eq(req.getType() != null, UserGroup::getType, req.getType())
					.orderByDesc(UserGroup::getCreateTime);
			IPage<UserGroup> iPage = userGroupService.page(req, query);

			dealData(iPage);
			return R.ok(iPage);
		} catch (Exception e) {
			log.error("用户管理-用户群组列表-接口：{}", e.getMessage());
			return R.failed("接口异常，请联系管理员");
		}
	}


	@ApiOperation(value = "用户群组列表", notes = "用户群组列表")
	@RequestMapping("/selectList")
	public R selectList(@Valid @RequestBody UserGroupReq req) {
		try {
			QueryWrapper<UserGroup> query = new QueryWrapper<>();

			query.lambda().and(StringUtils.isNotBlank(req.getGroupName()), wrapper -> wrapper.eq(UserGroup::getId, req.getGroupName()).or().like(UserGroup::getGroupName, req.getGroupName()))
					.eq(req.getGroupType() != null, UserGroup::getGroupType, req.getGroupType())
					.eq(req.getGroupClass() != null, UserGroup::getGroupClass, req.getGroupClass())
					.eq(req.getClassGameid() != null, UserGroup::getClassGameid, req.getClassGameid())
					.eq(req.getDimension() != null, UserGroup::getDimension, req.getDimension())
					.eq(req.getType() != null, UserGroup::getType, req.getType())
					//id；名称；筛选方式：1规则筛选，2ID筛选；筛选维度：1账号，2角色
					.select(UserGroup::getId, UserGroup::getGroupName, UserGroup::getType, UserGroup::getDimension, UserGroup::getGroupType, UserGroup::getGroupClass, UserGroup::getClassGameid)
					.orderByDesc(UserGroup::getCreateTime);
			List<UserGroup> list = userGroupService.list(query);
			return R.ok(list);
		} catch (Exception e) {
			log.error("用户管理-用户群组列表-接口：{}", e.getMessage());
			return R.failed("接口异常，请联系管理员");
		}
	}


	//处理列表中的创建人，使用真实姓名
	public void dealData(IPage<UserGroup> iPage) {
		if (Objects.nonNull(iPage) && !CollectionUtils.isEmpty(iPage.getRecords())) {
			List<UserGroup> list = iPage.getRecords();
			//获取当前操作用户ID集合
			Set<Long> userIds = list.stream().map(UserGroup::getCreateId).filter(Objects::nonNull).collect(Collectors.toSet());
			List<Integer> ids = userIds.stream().distinct().map(v -> v.intValue()).collect(Collectors.toList());
			R<List<SysUser>> userList = remoteUserService.getUserListByUserIds(SecurityConstants.FROM_IN, ids);
			//Map<用户ID,用户名称>
			Map<Integer, String> userMap = (userList == null || CollectionUtils.isEmpty(userList.getData())) ? new HashMap<>() : userList.getData().stream().collect(Collectors.toMap(SysUser::getUserId, SysUser::getRealName));

			// 获取群组分类-指定子游戏
			Set<Long> gameIds = list.stream().map(UserGroup::getClassGameid).filter(Objects::nonNull).collect(Collectors.toSet());
			List<WanGameDO> gameList = CollectionUtils.isEmpty(gameIds) ? new ArrayList<>() : gameService.list(Wrappers.<WanGameDO>query().lambda().in(WanGameDO::getId, gameIds));
			//Map<用户ID,用户名称>
			Map<Long, String> gameMap = CollectionUtils.isEmpty(gameList) ? new HashMap<>() : gameList.stream().collect(Collectors.toMap(WanGameDO::getId, WanGameDO::getGname));

			//用户群组ID
			List<Long> groupIds = list.stream().map(UserGroup::getId).collect(Collectors.toList());
			//查询这些群组的群组数量
			QueryWrapper<UserGroupData> wrapper = new QueryWrapper<UserGroupData>()
					.select("group_id as groupId, count(0) as groupUserNum")
					.in("group_id", groupIds)
					.groupBy("group_id");
			List<UserGroupData> groupUserNumList = CollectionUtils.isEmpty(groupIds) ? null : userGroupDataService.list(wrapper);
			Map<Long, Long> groupUserNumMap = CollectionUtils.isEmpty(groupUserNumList) ? new HashMap<>() : groupUserNumList.stream().collect(Collectors.toMap(UserGroupData::getGroupId, UserGroupData::getGroupUserNum));


			// 查询公告数、奖励数
			UserGroupStatReq req = new UserGroupStatReq();
			req.setGroupIds(groupIds);
			req.setQryType(UserGroupBindInfoQryTypeEnum.NOTICE_AND_PRIZE.getType());
			List<UserGroupStatVO> noticeList = userGroupService.getStatNums(req);

			list.forEach((item) -> {
				item.setCreateName(item.getCreateId() != null?userMap.get(item.getCreateId().intValue()):null);
				item.setGname(item.getClassGameid() != null?gameMap.get(item.getClassGameid()):null);
				item.setGroupUserNum(item.getUserNums());
				noticeList.stream().filter(b->item.getId().equals(b.getGroupId())).forEach(b->{
					item.setNoticeNums(b.getNoticeNums());
					item.setPrizeNums(b.getPrizeNums());
				});
				if(UserGroupTypeEnum.ID_FILTER.getType().equals(item.getType())){
					item.setGroupUserNum(groupUserNumMap.get(item.getId())==null?0L:groupUserNumMap.get(item.getId()));
				}

				});
			}
	}



	@ApiOperation(value = "删除用户群组", notes = "删除用户群组")
	@SysLog("删除用户群组")
	@PreAuthorize("@pms.hasPermission('usergroup_delete')")
	@NoRepeatSubmit(lockTime = 3000)
	@RequestMapping("/delete")
	public R delete(Long id){
		try {
			if (id == null){
				return R.failed("未获取到主键ID");
			}

			List<Long> groupIds = new ArrayList<>();
			groupIds.add(id);
			UserGroupStatReq req = new UserGroupStatReq();
			req.setQryType(4);
			req.setGroupIds(groupIds);
			List<UserGroupStatVO> noticeList = userGroupService.getStatNums(req);
			if(!CollectionUtils.isEmpty(noticeList)){
				return R.failed("该群组存在公告或奖励，不支持删除操作！");
			}

			//逻辑删除
			userGroupService.removeById(id);
			//增加记录
			UserGroupLog log = new UserGroupLog();
			log.setCreateId(SecurityUtils.getUser().getId().longValue());
			log.setType(3);
			log.setGroupId(id);
			log.setContent("");
			userGroupLogService.saveOrUpdate(log);
		} catch (Throwable t) {
			return R.failed(t.getMessage());
		}
		return R.ok();
	}


	@ApiOperation(value = "保存用户群组", notes = "保存用户群组")
	@SysLog("保存用户群组")
	@PreAuthorize("@pms.hasPermission('usergroup_save')")
	@NoRepeatSubmit(lockTime = 3000)
	@RequestMapping("/save")
	public R save(@Valid @RequestBody UserGroupVo vo){
		try {
			return userGroupService.saveOrUpdateUserGroup(vo);
		} catch (Throwable t) {
			return R.failed(t.getMessage());
		}
	}


	@ApiOperation(value = "用户群组详情", notes = "用户群组详情")
	@RequestMapping("/detail")
	@NoRepeatSubmit(lockTime = 3000)
	public R detail(@RequestBody UserGroupVo vo) {
		try {
			if (vo.getId() == null) {
				return R.failed("用户群组id不能为空");
			}
			UserGroup userGroup = userGroupService.getById(vo.getId());
			if (userGroup == null) {
				return R.failed("该记录不存在");
			}
			//查询对应的用户/群组列表
			return R.ok(userGroup);
		} catch (Exception e) {
			return R.failed("接口异常，请联系管理员");
		}
	}

	@ApiOperation(value = "修改用户群组状态信息", notes = "修改用户群组状态信息")
	@RequestMapping("/changeStatus")
	public R changeStatus(@Valid @RequestBody UserGroupUpdateStatusReq req) {
		try {
			if(req.getStatus() == null || (req.getOperType() == 1 && req.getStatus()!=1)){
				return R.failed("参数错误");
			}
			UserGroupStatusEnum curOperType = UserGroupStatusEnum.getOneByType(Integer.valueOf(req.getOperType()+""+req.getStatus()));
			if(UserGroupStatusEnum.UPDATE_MARK1.equals(curOperType)){
				// 更新列表
				return this.changeUpdateMark(req);
			}
			return R.failed("未知操作类型");

		} catch (Exception e) {
			log.error("用户管理-用户群组-修改用户群组状态信息接口：{}", e.getMessage());
			return R.failed("接口异常，请联系管理员");
		}
	}

	private R changeUpdateMark(UserGroupUpdateStatusReq req) {
		// 更新列表
		UserGroup data = userGroupService.getById(req.getGroupId());
		if(data == null){
			return R.failed("用户群组不存在");
		}
		if(data.getUpdateMark()!=null && data.getUpdateMark() == 1){
			return R.failed("计算中，请不要重复操作");
		}

		UserGroup userGroup = new UserGroup();
		userGroup.setId(req.getGroupId());
		userGroup.setUpdateMark(req.getStatus());
		userGroup.setLastUpdateTime(new Date());
		boolean result = userGroupService.updateByGroupId(userGroup);
		if(result){
			return R.ok();
		}
		return R.failed("接口异常，请联系管理员");
	}

	@ApiOperation(value = "修改用户群组状态信息(动心)", notes = "修改用户群组状态信息(动心)")
	@RequestMapping("/changeStatus4DX")
	public R changeStatus4DX(@Valid @RequestBody UserGroupUpdateStatusReq req) {
		try {
			log.info("changeStatus4DX:{}", JSON.toJSONString(req));

			if(req.getStatus() == null || req.getStatus()!=0 || req.getGroupUserNum() == null){
				return R.failed("参数错误");
			}
			UserGroup userGroupData = userGroupService.getById(req.getGroupId());
			if(userGroupData == null){
				//特定的1001，标识查询不到该用户群组，数据中心不做处理
				return R.failed(null, 1001,"未知用户群组ID");
			}
			UserGroup userGroup = new UserGroup();
			userGroup.setId(req.getGroupId());
			userGroup.setUpdateMark(req.getStatus());
			userGroup.setRefreshTime(new Date());
			if(UserGroupTypeEnum.RULE.getType().equals(userGroupData.getType())){
				userGroup.setUserNums(req.getGroupUserNum());
				if(req.getData_version() != null){
					userGroup.setDataVersion(req.getData_version());
				}
			}
			boolean result = userGroupService.updateByGroupId(userGroup);
			if(result){
				return R.ok();
			}
			return R.failed("接口异常，请联系管理员");
		} catch (Exception e) {
			log.error("用户管理-用户群组-修改用户群组状态信息(动心)接口：{}", e.getMessage());
			return R.failed("接口异常，请联系管理员");
		}
	}

	@ApiOperation(value = "查询用户群组的绑定详情(公告数、奖励数)", notes = "查询用户群组的绑定详情(公告数、奖励数)")
	@RequestMapping("/getStatNums")
	public R getStatNums(@Valid @RequestBody UserGroupStatReq req) {
		try {
			List<UserGroupStatVO> data = userGroupService.getStatNums(req);
			return R.ok(data);
		} catch (Exception e) {
			log.error("查询用户群组的绑定详情(公告数、奖励数)：{}", e.getMessage());
			return R.failed("接口异常，请联系管理员");
		}
	}

	@ApiOperation(value = "查询用户群组的绑定详情(公告IDS、奖励IDS)", notes = "查询用户群组的绑定详情(公告IDS、奖励IS)")
	@RequestMapping("/getBindNoticeList/{groupId}")
	public R getBindNoticeList(@PathVariable("groupId") Long groupId ) {
		try {
			UserGroupNoticePriceVO data = userGroupService.getBindNoticeList(groupId);
			return R.ok(data);
		} catch (Exception e) {
			log.error("查询用户群组的绑定详情(公告IDS、奖励IS)：{}", e.getMessage());
			return R.failed("接口异常，请联系管理员");
		}
	}

	@ApiOperation(value = "调用数据中心接口", notes = "调用数据中心接口")
	@RequestMapping("/dongxin/analyse")
	@NoRepeatSubmit(lockTime = 5000)
	public R dongxinAnalyse(@RequestBody UserGroupVo vo) {
		String lockKeyPrefix = "user_group_data:upload:lock:";
		String lockValue = "";
		if(vo.getId() != null){
			lockValue = redisLock.tryLock(lockKeyPrefix + vo.getId(), 120000);
			if(StringUtils.isBlank(lockValue)){
				return R.failed("请勿重复操作");
			}
		}
		try {

			Date now  = new Date();
			Integer qryType = null;  //查询类型，1 预估计算结果查询，2 手动更新结果查询


			log.info("vo:{}", JSON.toJSONString(vo));
			String dxAppid = yunYingProperties.getDongxinAppid();
			String url = yunYingProperties.getDongxinAnalyseUrl();
			Map<String,Object> param = new HashMap<>();
			param.put("dx_app_id", dxAppid);

			//预估计算
			if(Objects.isNull(vo.getId())){
				param.put("dimension", vo.getDimension());
				param.put("attribution",vo.getAttribution());
				param.put("behavior", vo.getBehavior());
				url += "/preCalculate";
				qryType = 1;
			}else{
				//更新列表
				url += "/updateCalculate";
				param.put("id", vo.getId());
				qryType = 2;
			}
			String resultStr = OEHttpUtils.doPost(url, param);
			log.info("resultStr:{}, url:{}, param:{}", resultStr, url, JSON.toJSONString(param));
			if(StringUtils.isBlank(resultStr)){
				return R.failed("计算失败!");
			}else{
				JSONObject obj = JSON.parseObject(resultStr);
				String code = obj.getString("code");
				if (StringUtils.equals(code, "0")) {
					//Long groupUserNum =  obj.getLong("groupUserNum") == null ? 0L : obj.getLong("groupUserNum");
					//Long dataVersion =  obj.getLong("data_version");
					String dataId = obj.getString("data_id");
					if(StringUtils.isBlank(dataId)){
						return R.failed("计算失败!");
					}
					//查询处理结果
					param.clear();
					param.put("dx_app_id", dxAppid);
					param.put("data_id", dataId);
					param.put("qry_type", qryType);
					TimeUnit.SECONDS.sleep(intervalTime);
					Map<String, Long> resultMap = fetchDataResult(param);
					if(MapUtils.isEmpty(resultMap)){
						return R.failed("还未获取到计算结果，请稍后再试!");
					}
					Long dataVersion = resultMap.get("dataVersion");
					Long groupUserNum = resultMap.get("groupUserNum");

					//预估计算
					if(Objects.isNull(vo.getId())){

					}else{
						if(MapUtils.isEmpty(resultMap)){
							return R.failed("还未获取到更新列表结果，请稍后再试!");
						}
						UserGroup userGroup = new UserGroup();
						userGroup.setId(vo.getId());
						userGroup.setLastUpdateTime(now);
						userGroup.setRefreshTime(now);
						userGroup.setUpdateMark(0);

						if(dataVersion !=null){
							userGroup.setDataVersion(dataVersion);
						}
						userGroup.setUserNums(groupUserNum);
						userGroupService.updateByGroupId(userGroup);
					}
					return R.ok(groupUserNum);
				}else{
					return R.failed(obj.getString("msg"));
				}
			}
		} catch (Exception e) {
			log.error("用户管理-用户群组数据列表-接口：{}", e.getMessage());
			return R.failed("接口异常，请联系管理员");
		}finally {
			if(vo.getId() != null){
				redisLock.unlock(lockKeyPrefix + vo.getId(), lockValue);
			}
		}
	}


	//最大重试次数
	private static final Integer tryTimes = 15;
	//重试间隔时间单位秒
	private static final Integer intervalTime = 3;

	/**
	 * 遍历获取计算结果
	 * @param param
	 * @return
	 */
	private Map<String, Long> fetchDataResult(Map<String, Object> param) {
		try {
			Map<String, Long> resultMap = new HashMap<>();
			String url = yunYingProperties.getDongxinAnalyseUrl();
			Integer retryNum = 1;
			while (retryNum <= tryTimes) {
				try {
					String resultStr = OEHttpUtils.doPost(url + "/preCalculateQry", param);
					log.info("preCalculateQry:resultStr:{}, url:{}, param:{}", resultStr, url + "/preCalculateQry", JSON.toJSONString(param));
					if(StringUtils.isBlank(resultStr)){
						return null;
					}else {
						JSONObject obj = JSON.parseObject(resultStr);
						String code = obj.getString("code");
						if (StringUtils.equals(code, "0")) {
							Integer calSuccess =  obj.getInteger("cal_success");
							//查询结果，1  成功， 2 查询中
							if(calSuccess != null && calSuccess == 1){
								Long groupUserNum =  obj.getLong("groupUserNum") == null ? 0L : obj.getLong("groupUserNum");
								Long dataVersion =  obj.getLong("data_version");
								resultMap.put("groupUserNum", groupUserNum);
								resultMap.put("dataVersion", dataVersion);
								return resultMap;
							}else{
								//继续计算
								log.info("{}计算未完成", calSuccess);
								TimeUnit.SECONDS.sleep(intervalTime);
								continue;
							}

						}else{
							//计算程序返回失败
							return null;
						}
					}
				} catch (Throwable e) {
					TimeUnit.SECONDS.sleep(intervalTime);
					continue;
				} finally {
					retryNum++;
				}

			}
		} catch (Throwable t){
			log.error(t.getMessage(), t);
			return null;
		}
		//没有得到计算结果
		return null;
	}

}
